﻿/*
 * Tencent is pleased to support the open source community by making xLua available.
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://opensource.org/licenses/MIT
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
using XLua.LuaDLL;
using UnityEngine.UI;

public enum UITypeEnum
{
    None,
    Text,
    Image,
    Button,
    Toggle,
    ScrollView,
    Custom,
    RawImage,
    PostEvent,
    Rect,
    Input,
    UnlimitedScroll,
    UICanvas,
}

[System.Serializable]
public class Injection
{
    public string name;
    public GameObject value;
    public UITypeEnum uiType;
}

[System.Serializable]
public class InjectioValue
{
    public string name;
    public string value;
}

public class LuaBehaviour : MonoBehaviour
{
    //public GameObject[] injections = null;
    public Injection[] injections2 = null;
    public InjectioValue[] injectioValues = null;
    public string moduleName = null;
    public string luaName = null;

    internal static float lastGCTime = 0;
    internal const float GCInterval = 1;//1 second 

    private Action<LuaTable> luaStart = null;
    private Action<LuaTable> luaUpdate = null;
    private Action<LuaTable> luaOnDestroy = null;
    private Action<LuaTable> luaAwake = null;
    private Action<LuaTable> luaOnEnable = null;
    private Action<LuaTable> luaOnDisable = null;
    
    protected LuaTable scriptEnv = null;

    void Awake()
    {
        if(moduleName==null || luaName==null )
        {
            return;
        }
        BindScriptEnv();
        BindScriptEnvParam();
        if (luaAwake != null)
        {
            luaAwake(scriptEnv);
        }
    }

    // 外部设置lua脚本
    public void SetScriptEnv(LuaTable env,bool bStart)
    {
        if(env==null)
        {
            return;
        }
        if(scriptEnv!=null)
        {
            return;
        }
        scriptEnv = env;
        BindScriptEnvParam();
        if (luaAwake != null)
        {
            luaAwake(scriptEnv);
        }
        if (bStart && luaStart != null)
        {
            luaStart(scriptEnv);
        }
    }

    // 绑定脚本参数
    private void BindScriptEnvParam()
    {
        if (scriptEnv == null)
        {
            return;
        }
        scriptEnv.Set("gameObject", gameObject);
        scriptEnv.Set("transform", transform);
        scriptEnv.Set("enabled", enabled);
        scriptEnv.Set("isDestroy", false);
        initPropertity();
        scriptEnv.Get("awake", out luaAwake);
        scriptEnv.Get("start", out luaStart);
        scriptEnv.Get("onDestroy", out luaOnDestroy);
        scriptEnv.Get("onEnable", out luaOnEnable);
        scriptEnv.Get("onDisable", out luaOnDisable);
    }

    protected virtual void BindScriptEnv()
    {

    }

    protected virtual void initPropertity()
    {
        if (injections2 != null)
        {
            foreach (var injection in injections2)
            {
                SetScriptInjecttion(injection.name, injection.value, injection.uiType);
            }
        }
        if (injectioValues != null)
        {
            foreach (var injecttion in injectioValues)
            {
                try
                {
                    var value = Convert.ToDouble(injecttion.value);
                    scriptEnv.Set<string, double>(injecttion.name, value);
                }
                catch (FormatException)
                {
                    scriptEnv.Set<string, string>(injecttion.name, injecttion.value);
                }
            }
        }
    }

    private void SetScriptInjecttion(string name, GameObject gameObject,UITypeEnum uiType)
    {
        switch(uiType)
        {
            case UITypeEnum.Custom:
                {
                    if (gameObject.scene.IsValid())
                    {
                        var luaBehaviour = gameObject.GetComponent<LuaBehaviour>();
                        if (luaBehaviour != null)
                        {
                            luaBehaviour.BindScriptEnv();
                            var luaScript = luaBehaviour.GetScriptEnv();
                            if (luaScript != null)
                            {
                                scriptEnv.Set(name, luaScript);
                            }
                        }
                    }
                }
                break;
            case UITypeEnum.Text:
                {
                    var text = gameObject.GetComponent<Text>();
                    scriptEnv.Set<string, Text>(name, text);
                }
                break;
            case UITypeEnum.Button:
                {
                    var button = gameObject.GetComponent<Button>();
                    scriptEnv.Set<string, Button>(name, button);
                }
                break;
            case UITypeEnum.Image:
                {
                    var image = gameObject.GetComponent<Image>();
                    scriptEnv.Set<string, Image>(name, image);
                }
                break;
            case UITypeEnum.Toggle:
                {
                    var toggle = gameObject.GetComponent<Toggle>();
                    scriptEnv.Set<string, Toggle>(name, toggle);
                }
                break;
            case UITypeEnum.ScrollView:
                {
                    var scrollView = gameObject.GetComponent<CommonScrollView>();
                    scriptEnv.Set<string, CommonScrollView>(name, scrollView);
                }
                break;
            case UITypeEnum.RawImage:
                {
                    var rawImage = gameObject.GetComponent<RawImage>();
                    scriptEnv.Set<string, RawImage>(name, rawImage);
                }
                break;
            case UITypeEnum.PostEvent:
                {
                    var postEvent = gameObject.GetComponent<GuidePostEvent>();
                    scriptEnv.Set<string, GuidePostEvent>(name, postEvent);
                }
                break;
            case UITypeEnum.Rect:
                {
                    var rect = gameObject.GetComponent<RectTransform>();
                    scriptEnv.Set<string, RectTransform>(name, rect);
                }
                break;
            case UITypeEnum.Input:
                {
                    var input = gameObject.GetComponent<InputField>();
                    scriptEnv.Set<string, InputField>(name, input);
                }
                break;
            case UITypeEnum.UnlimitedScroll:
                {
                    var scroll = gameObject.GetComponent<UnlimitedScroll>();
                    scriptEnv.Set<string, UnlimitedScroll>(name, scroll);
                }
                break;
            case UITypeEnum.UICanvas:
                {
                    var canvas = gameObject.GetComponent<Canvas>();
                    scriptEnv.Set<string, Canvas>(name, canvas);
                }
                break;
            default:
                {
                    scriptEnv.Set<string,GameObject>(name, gameObject);
                }
                break;
        }
    }

    public LuaTable GetScriptEnv()
    {
        return scriptEnv;
    }

    // Use this for initialization
    void Start()
    {
        if (scriptEnv != null)
        {
            UpdateBehaviour.AddUpdateScript(scriptEnv);
        }
        if (luaStart != null)
        {
            luaStart(scriptEnv);
        }
    }

    private void OnEnable()
    {
        if (scriptEnv != null)
            scriptEnv.Set("enabled", true);
        if (luaOnEnable!=null)
        {
            luaOnEnable(scriptEnv);
        }
    }

    private void OnDisable()
    {
        if (scriptEnv != null)
            scriptEnv.Set("enabled", false);
        if (luaOnDisable!=null)
        {
            luaOnDisable(scriptEnv);
        }
    }

    void CallScriptFunc(string funcName)
    {
        LuaFunction luaFunc = scriptEnv.Get<LuaFunction>(funcName);
        if (luaFunc!=null)
        {
            luaFunc.Call(scriptEnv);
        }
    }

    void OnDestroy()
    {
        if (scriptEnv != null)
        {
            UpdateBehaviour.RemoveUpdateScript(scriptEnv);
        }
        ClearScriptEnv();
    }

    public void ClearScriptEnv()
    {
        if (scriptEnv == null)
        {
            return;
        }
        scriptEnv.Set("isDestroy", true);
        if (luaOnDestroy != null)
        {
            luaOnDestroy(scriptEnv);
        }
        scriptEnv.Dispose();
        scriptEnv = null;
        luaOnDestroy = null;
        luaUpdate = null;
        luaStart = null;
    }

    private void OnTriggerEnter(Collider other)
    {
        //Debug.Log("OnTriggerEnter 1");
        Action <LuaTable,Collider> triggerEnter = null;
        scriptEnv.Get("onTriggerEnter", out triggerEnter);
        if (triggerEnter != null)
        {
            //Debug.Log("OnTriggerEnter 2");
            triggerEnter(scriptEnv, other);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        Action<LuaTable, Collider> triggerExit = null;
        scriptEnv.Get("onTriggerExit", out triggerExit);
        if (triggerExit != null)
        {
            triggerExit(scriptEnv, other);
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        Action<LuaTable, Collider2D> triggerEnter2D = null;
        scriptEnv.Get("onTriggerEnter2D", out triggerEnter2D);
        if (triggerEnter2D != null)
        {
            triggerEnter2D(scriptEnv, collision);
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        Action<LuaTable, Collider2D> triggerExit2D = null;
        scriptEnv.Get("onTriggerExit2D", out triggerExit2D);
        if (triggerExit2D != null)
        {
            triggerExit2D(scriptEnv, collision);
        }
    }

    private void OnTriggerStay(Collider other)
    {
        Action<LuaTable, Collider> triggerStay = null;
        scriptEnv.Get("onTriggerStay", out triggerStay);
        if (triggerStay != null)
        {
            triggerStay(scriptEnv, other);
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        Action<LuaTable, Collider2D> triggerStay2D = null;
        scriptEnv.Get("onTriggerStay2D", out triggerStay2D);
        if (triggerStay2D != null)
        {
            triggerStay2D(scriptEnv, collision);
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        Action<LuaTable, Collision> collisionEnter = null;
        scriptEnv.Get("onCollisionEnter", out collisionEnter);
        if (collisionEnter != null)
        {
            collisionEnter(scriptEnv, collision);
        }
    }

    private void OnCollisionExit(Collision collision)
    {
        Action<LuaTable, Collision> collisionExit = null;
        scriptEnv.Get("onCollisionExit", out collisionExit);
        if (collisionExit != null)
        {
            collisionExit(scriptEnv, collision);
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        Action<LuaTable, Collision2D> collisionEnter2D = null;
        scriptEnv.Get("onCollisionEnter2D", out collisionEnter2D);
        if (collisionEnter2D != null)
        {
            collisionEnter2D(scriptEnv, collision);
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        Action<LuaTable, Collision2D> collisionExit2D = null;
        scriptEnv.Get("onCollisionExit2D", out collisionExit2D);
        if (collisionExit2D != null)
        {
            collisionExit2D(scriptEnv, collision);
        }
    }

    private void OnCollisionStay(Collision collision)
    {
        Action<LuaTable, Collision> collisionStay = null;
        scriptEnv.Get("onCollisionStay", out collisionStay);
        if (collisionStay != null)
        {
            collisionStay(scriptEnv, collision);
        }
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        Action<LuaTable, Collision2D> collisionStay2D = null;
        scriptEnv.Get("onCollisionStay2D", out collisionStay2D);
        if (collisionStay2D != null)
        {
            collisionStay2D(scriptEnv, collision);
        }
    }
}
